@@ -5,8 +5,8 @@ from django.contrib import admin  | 
            ||
| 5 | 5 | 
                from django.contrib.auth.hashers import make_password  | 
            
| 6 | 6 | 
                from django.db import transaction  | 
            
| 7 | 7 | 
                 | 
            
| 8 | 
                -from account.models import (LensmanIncomeExpensesInfo, LensmanInfo, LensmanLoginLogInfo, UserIncomeExpensesInfo,  | 
            |
| 9 | 
                - UserInfo, UserLoginLogInfo)  | 
            |
| 8 | 
                +from account.models import (LensmanIncomeExpensesInfo, LensmanInfo, LensmanLoginLogInfo, TourGuideInfo,  | 
            |
| 9 | 
                + UserIncomeExpensesInfo, UserInfo, UserLoginLogInfo)  | 
            |
| 10 | 10 | 
                 | 
            
| 11 | 11 | 
                 | 
            
| 12 | 12 | 
                class LensmanInfoAdmin(admin.ModelAdmin):  | 
            
                @@ -31,10 +31,12 @@ class LensmanInfoAdmin(admin.ModelAdmin):  | 
            ||
| 31 | 31 | 
                'sex': obj.sex,  | 
            
| 32 | 32 | 
                'phone': obj.phone,  | 
            
| 33 | 33 | 
                'location': obj.location,  | 
            
| 34 | 
                + 'islensman': True,  | 
            |
| 34 | 35 | 
                'user_status': obj.user_status,  | 
            
| 35 | 36 | 
                }  | 
            
| 36 | 37 | 
                user, created = UserInfo.objects.get_or_create(user_id=obj.lensman_id, defaults=fields)  | 
            
| 37 | 38 | 
                if not created:  | 
            
| 39 | 
                +            fields.pop('user_from', None)
               | 
            |
| 38 | 40 | 
                for key, value in fields.iteritems():  | 
            
| 39 | 41 | 
                setattr(user, key, value)  | 
            
| 40 | 42 | 
                user.save()  | 
            
                @@ -54,6 +56,40 @@ class LensmanIncomeExpensesInfoAdmin(admin.ModelAdmin):  | 
            ||
| 54 | 56 | 
                     list_filter = ('type', 'status')
               | 
            
| 55 | 57 | 
                 | 
            
| 56 | 58 | 
                 | 
            
| 59 | 
                +class TourGuideInfoAdmin(admin.ModelAdmin):  | 
            |
| 60 | 
                +    readonly_fields = ('tourguide_id', )
               | 
            |
| 61 | 
                +    list_display = ('tourguide_id', 'unionid', 'name', 'sex', 'phone', 'location', 'no', 'user_status', 'status', 'created_at', 'updated_at')
               | 
            |
| 62 | 
                +    search_fields = ('name', 'phone', 'location', 'no')
               | 
            |
| 63 | 
                +    list_filter = ('sex', 'user_status', 'status')
               | 
            |
| 64 | 
                +  | 
            |
| 65 | 
                + @transaction.atomic  | 
            |
| 66 | 
                + def save_model(self, request, obj, form, change):  | 
            |
| 67 | 
                + if not obj.tourguide_id:  | 
            |
| 68 | 
                + try:  | 
            |
| 69 | 
                + user_id = UserInfo.objects.select_for_update().get(unionid=obj.unionid).user_id  | 
            |
| 70 | 
                + except UserInfo.DoesNotExist:  | 
            |
| 71 | 
                + user_id = None  | 
            |
| 72 | 
                + obj.tourguide_id = user_id or CurtailUUID.uuid(UserInfo, 'user_id')  | 
            |
| 73 | 
                + obj.save()  | 
            |
| 74 | 
                +  | 
            |
| 75 | 
                +        fields = {
               | 
            |
| 76 | 
                + 'user_from': UserInfo.TOURGUIDE_USER,  | 
            |
| 77 | 
                + 'unionid': obj.unionid,  | 
            |
| 78 | 
                + 'name': obj.name,  | 
            |
| 79 | 
                + 'sex': obj.sex,  | 
            |
| 80 | 
                + 'phone': obj.phone,  | 
            |
| 81 | 
                + 'location': obj.location,  | 
            |
| 82 | 
                + 'istourguide': True,  | 
            |
| 83 | 
                + 'user_status': obj.user_status,  | 
            |
| 84 | 
                + }  | 
            |
| 85 | 
                + user, created = UserInfo.objects.get_or_create(user_id=obj.tourguide_id, defaults=fields)  | 
            |
| 86 | 
                + if not created:  | 
            |
| 87 | 
                +            fields.pop('user_from', None)
               | 
            |
| 88 | 
                + for key, value in fields.iteritems():  | 
            |
| 89 | 
                + setattr(user, key, value)  | 
            |
| 90 | 
                + user.save()  | 
            |
| 91 | 
                +  | 
            |
| 92 | 
                +  | 
            |
| 57 | 93 | 
                class UserInfoAdmin(admin.ModelAdmin):  | 
            
| 58 | 94 | 
                     readonly_fields = ('user_id', )
               | 
            
| 59 | 95 | 
                     list_display = ('user_id', 'user_from', 'username', 'openid', 'unionid', 'name', 'sex', 'nickname', 'phone', 'location', 'balance', 'user_status', 'status', 'created_at', 'updated_at')
               | 
            
                @@ -73,6 +109,7 @@ class UserIncomeExpensesInfoAdmin(admin.ModelAdmin):  | 
            ||
| 73 | 109 | 
                admin.site.register(LensmanInfo, LensmanInfoAdmin)  | 
            
| 74 | 110 | 
                admin.site.register(LensmanLoginLogInfo, LensmanLoginLogInfoAdmin)  | 
            
| 75 | 111 | 
                admin.site.register(LensmanIncomeExpensesInfo, LensmanIncomeExpensesInfoAdmin)  | 
            
| 112 | 
                +admin.site.register(TourGuideInfo, TourGuideInfoAdmin)  | 
            |
| 76 | 113 | 
                admin.site.register(UserInfo, UserInfoAdmin)  | 
            
| 77 | 114 | 
                admin.site.register(UserLoginLogInfo, UserLoginLogInfoAdmin)  | 
            
| 78 | 115 | 
                admin.site.register(UserIncomeExpensesInfo, UserIncomeExpensesInfoAdmin)  | 
            
                @@ -0,0 +1,36 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +from __future__ import unicode_literals  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +from django.db import models, migrations  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +class Migration(migrations.Migration):  | 
            |
| 8 | 
                +  | 
            |
| 9 | 
                + dependencies = [  | 
            |
| 10 | 
                +        ('account', '0019_auto_20161024_1235'),
               | 
            |
| 11 | 
                + ]  | 
            |
| 12 | 
                +  | 
            |
| 13 | 
                + operations = [  | 
            |
| 14 | 
                + migrations.CreateModel(  | 
            |
| 15 | 
                + name='TourGuideInfo',  | 
            |
| 16 | 
                + fields=[  | 
            |
| 17 | 
                +                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
               | 
            |
| 18 | 
                +                ('status', models.BooleanField(default=True, help_text='\u72b6\u6001', db_index=True, verbose_name='status')),
               | 
            |
| 19 | 
                +                ('created_at', models.DateTimeField(help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at', auto_now_add=True)),
               | 
            |
| 20 | 
                +                ('updated_at', models.DateTimeField(help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at', auto_now=True)),
               | 
            |
| 21 | 
                +                ('tourguide_id', models.CharField(null=True, max_length=255, blank=True, help_text='\u5bfc\u6e38\u552f\u4e00\u6807\u8bc6', unique=True, verbose_name='tourguide_id', db_index=True)),
               | 
            |
| 22 | 
                +                ('unionid', models.CharField(null=True, max_length=255, blank=True, help_text='\u5fae\u4fe1 Union ID', unique=True, verbose_name='unionid', db_index=True)),
               | 
            |
| 23 | 
                +                ('name', models.CharField(help_text='\u5bfc\u6e38\u59d3\u540d', max_length=255, null=True, verbose_name='name', blank=True)),
               | 
            |
| 24 | 
                +                ('sex', models.IntegerField(default=1, help_text='\u5bfc\u6e38\u6027\u522b', verbose_name='sex', choices=[(1, '\u7537'), (0, '\u5973')])),
               | 
            |
| 25 | 
                +                ('phone', models.CharField(null=True, max_length=255, blank=True, help_text='\u5bfc\u6e38\u7535\u8bdd', unique=True, verbose_name='phone', db_index=True)),
               | 
            |
| 26 | 
                +                ('location', models.CharField(help_text='\u5bfc\u6e38\u5730\u5740', max_length=255, null=True, verbose_name='location', blank=True)),
               | 
            |
| 27 | 
                +                ('no', models.CharField(help_text='\u5bfc\u6e38\u8bc1\u7f16\u53f7', max_length=255, null=True, verbose_name='no', blank=True)),
               | 
            |
| 28 | 
                +                ('user_status', models.IntegerField(default=0, verbose_name='user_status', choices=[(-1, '\u5df2\u62d2\u7edd'), (0, '\u672a\u9a8c\u8bc1'), (1, '\u5df2\u6fc0\u6d3b'), (2, '\u5df2\u7981\u7528'), (3, '\u5df2\u5220\u9664'), (10, '\u5df2\u5206\u914d')])),
               | 
            |
| 29 | 
                +                ('refused_reason', models.TextField(help_text='\u5ba1\u6838\u62d2\u7edd\u539f\u56e0', null=True, verbose_name='refused_reason', blank=True)),
               | 
            |
| 30 | 
                + ],  | 
            |
| 31 | 
                +            options={
               | 
            |
| 32 | 
                + 'verbose_name': 'tourguideinfo',  | 
            |
| 33 | 
                + 'verbose_name_plural': 'tourguideinfo',  | 
            |
| 34 | 
                + },  | 
            |
| 35 | 
                + ),  | 
            |
| 36 | 
                + ]  | 
            
                @@ -0,0 +1,29 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +from __future__ import unicode_literals  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +from django.db import models, migrations  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +class Migration(migrations.Migration):  | 
            |
| 8 | 
                +  | 
            |
| 9 | 
                + dependencies = [  | 
            |
| 10 | 
                +        ('account', '0020_tourguideinfo'),
               | 
            |
| 11 | 
                + ]  | 
            |
| 12 | 
                +  | 
            |
| 13 | 
                + operations = [  | 
            |
| 14 | 
                + migrations.AddField(  | 
            |
| 15 | 
                + model_name='userinfo',  | 
            |
| 16 | 
                + name='islensman',  | 
            |
| 17 | 
                + field=models.BooleanField(default=False, help_text='\u6444\u5f71\u5e08\uff1f', db_index=True, verbose_name='islensman'),  | 
            |
| 18 | 
                + ),  | 
            |
| 19 | 
                + migrations.AddField(  | 
            |
| 20 | 
                + model_name='userinfo',  | 
            |
| 21 | 
                + name='istourguide',  | 
            |
| 22 | 
                + field=models.BooleanField(default=False, help_text='\u5bfc\u6e38\uff1f', db_index=True, verbose_name='istourguide'),  | 
            |
| 23 | 
                + ),  | 
            |
| 24 | 
                + migrations.AlterField(  | 
            |
| 25 | 
                + model_name='userinfo',  | 
            |
| 26 | 
                + name='user_from',  | 
            |
| 27 | 
                + field=models.IntegerField(default=0, help_text='\u7528\u6237\u6765\u6e90', verbose_name='user_from', choices=[(0, 'APP \u521b\u5efa\u7528\u6237'), (1, '\u5fae\u4fe1\u6388\u6743\u7528\u6237'), (9, '\u6e38\u5ba2\u7528\u6237'), (10, '\u6444\u5f71\u5e08\u7528\u6237'), (11, '\u5bfc\u6e38\u7528\u6237')]),  | 
            |
| 28 | 
                + ),  | 
            |
| 29 | 
                + ]  | 
            
                @@ -135,6 +135,64 @@ class LensmanIncomeExpensesInfo(CreateUpdateMixin):  | 
            ||
| 135 | 135 | 
                return unicode(self.pk)  | 
            
| 136 | 136 | 
                 | 
            
| 137 | 137 | 
                 | 
            
| 138 | 
                +class TourGuideInfo(CreateUpdateMixin):  | 
            |
| 139 | 
                + MALE = 1  | 
            |
| 140 | 
                + FEMALE = 0  | 
            |
| 141 | 
                +  | 
            |
| 142 | 
                + SEX_TYPE = (  | 
            |
| 143 | 
                + (MALE, u'男'),  | 
            |
| 144 | 
                + (FEMALE, u'女'),  | 
            |
| 145 | 
                + )  | 
            |
| 146 | 
                +  | 
            |
| 147 | 
                + REFUSED = -1  | 
            |
| 148 | 
                + UNVERIFIED = 0  | 
            |
| 149 | 
                + ACTIVATED = 1  | 
            |
| 150 | 
                + DISABLED = 2  | 
            |
| 151 | 
                + DELETED = 3  | 
            |
| 152 | 
                + ASSIGN = 10  | 
            |
| 153 | 
                +  | 
            |
| 154 | 
                + USER_STATUS = (  | 
            |
| 155 | 
                + (REFUSED, u'已拒绝'),  | 
            |
| 156 | 
                + (UNVERIFIED, u'未验证'),  | 
            |
| 157 | 
                + (ACTIVATED, u'已激活'),  | 
            |
| 158 | 
                + (DISABLED, u'已禁用'),  | 
            |
| 159 | 
                + (DELETED, u'已删除'),  | 
            |
| 160 | 
                + (ASSIGN, u'已分配'),  | 
            |
| 161 | 
                + )  | 
            |
| 162 | 
                +  | 
            |
| 163 | 
                + tourguide_id = models.CharField(_(u'tourguide_id'), max_length=255, blank=True, null=True, help_text=u'导游唯一标识', db_index=True, unique=True)  | 
            |
| 164 | 
                +  | 
            |
| 165 | 
                + unionid = models.CharField(_(u'unionid'), max_length=255, blank=True, null=True, help_text=u'微信 Union ID', db_index=True, unique=True)  | 
            |
| 166 | 
                +  | 
            |
| 167 | 
                + name = models.CharField(_(u'name'), max_length=255, blank=True, null=True, help_text=u'导游姓名')  | 
            |
| 168 | 
                + sex = models.IntegerField(_(u'sex'), choices=SEX_TYPE, default=MALE, help_text=u'导游性别')  | 
            |
| 169 | 
                + phone = models.CharField(_(u'phone'), max_length=255, blank=True, null=True, help_text=u'导游电话', db_index=True, unique=True)  | 
            |
| 170 | 
                + location = models.CharField(_(u'location'), max_length=255, blank=True, null=True, help_text=u'导游地址')  | 
            |
| 171 | 
                +  | 
            |
| 172 | 
                + no = models.CharField(_(u'no'), max_length=255, blank=True, null=True, help_text=u'导游证编号')  | 
            |
| 173 | 
                +  | 
            |
| 174 | 
                + user_status = models.IntegerField(_(u'user_status'), choices=USER_STATUS, default=UNVERIFIED)  | 
            |
| 175 | 
                + refused_reason = models.TextField(_(u'refused_reason'), blank=True, null=True, help_text=u'审核拒绝原因')  | 
            |
| 176 | 
                +  | 
            |
| 177 | 
                + class Meta:  | 
            |
| 178 | 
                + verbose_name = _(u'tourguideinfo')  | 
            |
| 179 | 
                + verbose_name_plural = _(u'tourguideinfo')  | 
            |
| 180 | 
                +  | 
            |
| 181 | 
                + def __unicode__(self):  | 
            |
| 182 | 
                + return unicode(self.pk)  | 
            |
| 183 | 
                +  | 
            |
| 184 | 
                + @property  | 
            |
| 185 | 
                + def data(self):  | 
            |
| 186 | 
                +        return {
               | 
            |
| 187 | 
                + 'name': self.name,  | 
            |
| 188 | 
                + 'sex': self.sex,  | 
            |
| 189 | 
                + 'phone': self.phone,  | 
            |
| 190 | 
                + 'location': self.location,  | 
            |
| 191 | 
                + 'status': self.user_status,  | 
            |
| 192 | 
                + 'refused_reason': self.refused_reason,  | 
            |
| 193 | 
                + }  | 
            |
| 194 | 
                +  | 
            |
| 195 | 
                +  | 
            |
| 138 | 196 | 
                class WechatInfo(CreateUpdateMixin):  | 
            
| 139 | 197 | 
                MALE = 1  | 
            
| 140 | 198 | 
                FEMALE = 0  | 
            
                @@ -167,12 +225,14 @@ class UserInfo(CreateUpdateMixin):  | 
            ||
| 167 | 225 | 
                WX_USER = 1  | 
            
| 168 | 226 | 
                GUEST_USER = 9  | 
            
| 169 | 227 | 
                LENSMAN_USER = 10  | 
            
| 228 | 
                + TOURGUIDE_USER = 11  | 
            |
| 170 | 229 | 
                 | 
            
| 171 | 230 | 
                USER_FROM = (  | 
            
| 172 | 231 | 
                (APP_USER, u'APP 创建用户'),  | 
            
| 173 | 232 | 
                (WX_USER, u'微信授权用户'),  | 
            
| 174 | 233 | 
                (GUEST_USER, u'游客用户'),  | 
            
| 175 | 234 | 
                (LENSMAN_USER, u'摄影师用户'),  | 
            
| 235 | 
                + (TOURGUIDE_USER, u'导游用户'),  | 
            |
| 176 | 236 | 
                )  | 
            
| 177 | 237 | 
                 | 
            
| 178 | 238 | 
                UNVERIFIED = 0  | 
            
                @@ -218,6 +278,10 @@ class UserInfo(CreateUpdateMixin):  | 
            ||
| 218 | 278 | 
                city = models.CharField(_(u'city'), max_length=255, blank=True, null=True, help_text=u'用户城市')  | 
            
| 219 | 279 | 
                location = models.CharField(_(u'location'), max_length=255, blank=True, null=True, help_text=u'用户地址')  | 
            
| 220 | 280 | 
                 | 
            
| 281 | 
                + # 用户身份  | 
            |
| 282 | 
                + islensman = models.BooleanField(_(u'islensman'), default=False, help_text=_(u'摄影师?'), db_index=True)  | 
            |
| 283 | 
                + istourguide = models.BooleanField(_(u'istourguide'), default=False, help_text=_(u'导游?'), db_index=True)  | 
            |
| 284 | 
                +  | 
            |
| 221 | 285 | 
                balance = models.IntegerField(_(u'balance'), default=0, help_text=u'用户余额(分)')  | 
            
| 222 | 286 | 
                freeze_income_balance = models.IntegerField(_(u'freeze_income_balance'), default=0, help_text=u'用户收入冻结余额(分)')  | 
            
| 223 | 287 | 
                freeze_expense_balance = models.IntegerField(_(u'freeze_expense_balance'), default=0, help_text=u'用户支出冻结余额(分)')  | 
            
                @@ -250,6 +314,8 @@ class UserInfo(CreateUpdateMixin):  | 
            ||
| 250 | 314 | 
                return self.nickname  | 
            
| 251 | 315 | 
                elif self.user_from == self.LENSMAN_USER:  | 
            
| 252 | 316 | 
                return self.name  | 
            
| 317 | 
                + elif self.user_from == self.TOURGUIDE_USER:  | 
            |
| 318 | 
                + return self.name  | 
            |
| 253 | 319 | 
                return self.nickname  | 
            
| 254 | 320 | 
                 | 
            
| 255 | 321 | 
                @property  | 
            
                @@ -0,0 +1,48 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from __future__ import division  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from django.conf import settings  | 
            |
| 6 | 
                +from logit import logit  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +from account.models import TourGuideInfo  | 
            |
| 9 | 
                +from utils.error.errno_utils import TourGuideStatusCode  | 
            |
| 10 | 
                +from utils.error.response_utils import response  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                +  | 
            |
| 13 | 
                +r = settings.REDIS_CACHE  | 
            |
| 14 | 
                +  | 
            |
| 15 | 
                +  | 
            |
| 16 | 
                +@logit  | 
            |
| 17 | 
                +def tourguide_submit_api(request):  | 
            |
| 18 | 
                + """  | 
            |
| 19 | 
                + 导游信息提交  | 
            |
| 20 | 
                + :param request:  | 
            |
| 21 | 
                + :return:  | 
            |
| 22 | 
                + """  | 
            |
| 23 | 
                +    unionid = request.POST.get('unionid', '')
               | 
            |
| 24 | 
                +    openid = request.POST.get('openid', '')
               | 
            |
| 25 | 
                +    phone = request.POST.get('phone', '')
               | 
            |
| 26 | 
                +  | 
            |
| 27 | 
                + if TourGuideInfo.objects.filter(phone=phone).exclude(unionid=unionid).exists():  | 
            |
| 28 | 
                + return response(TourGuideStatusCode.TOURGUIDE_PHONE_ALREADY_EXISTS)  | 
            |
| 29 | 
                +  | 
            |
| 30 | 
                +    fields = {
               | 
            |
| 31 | 
                +        'name': request.POST.get('name', ''),
               | 
            |
| 32 | 
                +        'sex': int(request.POST.get('sex', 1)),
               | 
            |
| 33 | 
                + 'phone': phone,  | 
            |
| 34 | 
                +        'location': request.POST.get('location', ''),
               | 
            |
| 35 | 
                +        'no': request.POST.get('no', ''),
               | 
            |
| 36 | 
                + 'user_status': TourGuideInfo.UNVERIFIED,  | 
            |
| 37 | 
                + }  | 
            |
| 38 | 
                +  | 
            |
| 39 | 
                + tourguide, created = TourGuideInfo.objects.get_or_create(unionid=unionid, defaults=fields)  | 
            |
| 40 | 
                + # 状态为 UNVERIFIED 的允许修改, 其他需要登录摄影师 APP 进行信息的修改  | 
            |
| 41 | 
                + if tourguide.user_status not in [TourGuideInfo.UNVERIFIED, TourGuideInfo.REFUSED]:  | 
            |
| 42 | 
                + return response(TourGuideStatusCode.TOURGUIDE_ALREADY_NOT_UNVERIFIED)  | 
            |
| 43 | 
                + if not created:  | 
            |
| 44 | 
                + for key, value in fields.iteritems():  | 
            |
| 45 | 
                + setattr(tourguide, key, value)  | 
            |
| 46 | 
                + tourguide.save()  | 
            |
| 47 | 
                +  | 
            |
| 48 | 
                +    return response(200, 'Submit Success', u'提交成功', {})
               | 
            
                @@ -39,7 +39,7 @@ def lensman_login_api(request):  | 
            ||
| 39 | 39 | 
                 | 
            
| 40 | 40 | 
                @logit  | 
            
| 41 | 41 | 
                def user_is_registered_api(request):  | 
            
| 42 | 
                -    return response(200, '', '', {
               | 
            |
| 42 | 
                +    return response(200, data={
               | 
            |
| 43 | 43 | 
                         'registered': UserInfo.objects.filter(username=request.POST.get('username', '')).exists(),
               | 
            
| 44 | 44 | 
                })  | 
            
| 45 | 45 | 
                 | 
            
                @@ -3,6 +3,7 @@  | 
            ||
| 3 | 3 | 
                from django.conf.urls import url  | 
            
| 4 | 4 | 
                 | 
            
| 5 | 5 | 
                from account import views as account_views  | 
            
| 6 | 
                +from account import tourguide_views  | 
            |
| 6 | 7 | 
                from group import views as group_views  | 
            
| 7 | 8 | 
                from group import lensman_views  | 
            
| 8 | 9 | 
                from message import views as message_views  | 
            
                @@ -41,6 +42,11 @@ urlpatterns += [  | 
            ||
| 41 | 42 | 
                url(r'^l/origin_wanted$', lensman_views.lensman_origin_wanted_api, name='lensman_origin_wanted_api'), # 摄影师原图订单  | 
            
| 42 | 43 | 
                ]  | 
            
| 43 | 44 | 
                 | 
            
| 45 | 
                +# 导游相关  | 
            |
| 46 | 
                +urlpatterns += [  | 
            |
| 47 | 
                + url(r'^t/submit$', tourguide_views.tourguide_submit_api, name='tourguide_submit_api'), # 导游信息提交  | 
            |
| 48 | 
                +]  | 
            |
| 49 | 
                +  | 
            |
| 44 | 50 | 
                # 群组相关  | 
            
| 45 | 51 | 
                urlpatterns += [  | 
            
| 46 | 52 | 
                url(r'^g/create$', group_views.group_create_api, name='group_create_api'), # 群组创建  | 
            
                @@ -0,0 +1,213 @@  | 
            ||
| 1 | 
                +{% load staticfiles %}
               | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +<!DOCTYPE html>  | 
            |
| 4 | 
                +<html lang="zh-CN">  | 
            |
| 5 | 
                + <head>  | 
            |
| 6 | 
                + <meta charset="utf-8">  | 
            |
| 7 | 
                + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">  | 
            |
| 8 | 
                + <meta name="format-detection" content="telephone=no,email=no,address=no">  | 
            |
| 9 | 
                + <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">  | 
            |
| 10 | 
                + <title>导游授权</title>  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + <link href="https://res.wx.qq.com/open/libs/weui/0.4.3/weui.min.css" rel="stylesheet" type="text/css" />  | 
            |
| 13 | 
                +  | 
            |
| 14 | 
                + <style>  | 
            |
| 15 | 
                +            input:required:invalid {
               | 
            |
| 16 | 
                + color: #E64340;  | 
            |
| 17 | 
                + }  | 
            |
| 18 | 
                +            input:required:valid {
               | 
            |
| 19 | 
                + color: rgb(0, 0, 0);  | 
            |
| 20 | 
                + }  | 
            |
| 21 | 
                + </style>  | 
            |
| 22 | 
                + </head>  | 
            |
| 23 | 
                + <body>  | 
            |
| 24 | 
                + <div class="container" >  | 
            |
| 25 | 
                + <div class="weui_cells_title">基本信息</div>  | 
            |
| 26 | 
                + <div class="weui_cells weui_cells_form">  | 
            |
| 27 | 
                + <div class="weui_cell">  | 
            |
| 28 | 
                + <div class="weui_cell_hd"><label for="" class="weui_label">姓名</label></div>  | 
            |
| 29 | 
                + <div class="weui_cell_bd weui_cell_primary">  | 
            |
| 30 | 
                +                        <input id="name" class="weui_input" type="text" value="{{ tourguide_info.name }}" placeholder="请输入姓名" {% if not modified %}disabled{% endif %}>
               | 
            |
| 31 | 
                + </div>  | 
            |
| 32 | 
                + </div>  | 
            |
| 33 | 
                + <div class="weui_cell weui_cell_select weui_select_after">  | 
            |
| 34 | 
                + <div class="weui_cell_hd"><label for="" class="weui_label">性别</label></div>  | 
            |
| 35 | 
                + <div class="weui_cell_bd weui_cell_primary">  | 
            |
| 36 | 
                +                        <select id="sex" class="weui_select" name="select" {% if not modified %}disabled{% endif %}>
               | 
            |
| 37 | 
                +                            <option value="1" {% ifequal tourguide_info.sex 1 %}selected{% endifequal %}>男</option>
               | 
            |
| 38 | 
                +                            <option value="0" {% ifequal tourguide_info.sex 0 %}selected{% endifequal %}>女</option>
               | 
            |
| 39 | 
                + </select>  | 
            |
| 40 | 
                + </div>  | 
            |
| 41 | 
                + </div>  | 
            |
| 42 | 
                + <div class="weui_cell">  | 
            |
| 43 | 
                + <div class="weui_cell_hd"><label for="" class="weui_label">手机号</label></div>  | 
            |
| 44 | 
                + <div class="weui_cell_bd weui_cell_primary">  | 
            |
| 45 | 
                +                        <input id="phone" class="weui_input" type="text" required="required" pattern="[0-9]{11}" value="{{ tourguide_info.phone }}" placeholder="请输入手机号" {% if not modified %}disabled{% endif %}>
               | 
            |
| 46 | 
                + </div>  | 
            |
| 47 | 
                + </div>  | 
            |
| 48 | 
                +{#                <div class="weui_cell">#}
               | 
            |
| 49 | 
                +{#                    <div class="weui_cell_hd"><label for="" class="weui_label">地址</label></div>#}
               | 
            |
| 50 | 
                +{#                    <div class="weui_cell_bd weui_cell_primary">#}
               | 
            |
| 51 | 
                +{#                        <input id="location" class="weui_input" type="text" value="{{ tourguide_info.location }}" placeholder="请输入地址" {% if not modified %}disabled{% endif %}>#}
               | 
            |
| 52 | 
                +{#                    </div>#}
               | 
            |
| 53 | 
                +{#                </div>#}
               | 
            |
| 54 | 
                + <div class="weui_cell">  | 
            |
| 55 | 
                + <div class="weui_cell_hd"><label for="" class="weui_label">导游证编号</label></div>  | 
            |
| 56 | 
                + <div class="weui_cell_bd weui_cell_primary">  | 
            |
| 57 | 
                +                        <input id="no" class="weui_input" type="text" required="required" value="{{ tourguide_info.no }}" placeholder="请输入导游证编号" {% if not modified %}disabled{% endif %}>
               | 
            |
| 58 | 
                + </div>  | 
            |
| 59 | 
                + </div>  | 
            |
| 60 | 
                + </div>  | 
            |
| 61 | 
                +  | 
            |
| 62 | 
                +            {% if tourguide_info %}
               | 
            |
| 63 | 
                + <div class="weui_cells_title">审核状态</div>  | 
            |
| 64 | 
                + <div class="weui_cells">  | 
            |
| 65 | 
                + <div class="weui_cell">  | 
            |
| 66 | 
                + <div class="weui_cell_bd weui_cell_primary">  | 
            |
| 67 | 
                + <p>状态</p>  | 
            |
| 68 | 
                + </div>  | 
            |
| 69 | 
                + <div class="weui_cell_ft">  | 
            |
| 70 | 
                +                        {% ifequal tourguide_info.status -1 %}已拒绝{% endifequal %}
               | 
            |
| 71 | 
                +                        {% ifequal tourguide_info.status 0 %}审核中{% endifequal %}
               | 
            |
| 72 | 
                +                        {% ifequal tourguide_info.status 1 %}已激活{% endifequal %}
               | 
            |
| 73 | 
                +                        {% ifequal tourguide_info.status 2 %}已禁用{% endifequal %}
               | 
            |
| 74 | 
                +                        {% ifequal tourguide_info.status 3 %}已删除{% endifequal %}
               | 
            |
| 75 | 
                + </div>  | 
            |
| 76 | 
                + </div>  | 
            |
| 77 | 
                + </div>  | 
            |
| 78 | 
                +            {% endif %}
               | 
            |
| 79 | 
                +  | 
            |
| 80 | 
                +  | 
            |
| 81 | 
                +            {% ifequal tourguide_info.status -1 %}
               | 
            |
| 82 | 
                + <div class="weui_cells_title">拒绝原因</div>  | 
            |
| 83 | 
                + <div class="weui_cells">  | 
            |
| 84 | 
                + <div class="weui_panel_bd">  | 
            |
| 85 | 
                + <div class="weui_media_box weui_media_text">  | 
            |
| 86 | 
                +                        <p class="weui_media_desc">{{ tourguide_info.refused_reason|safe|linebreaks }}</p>
               | 
            |
| 87 | 
                + </div>  | 
            |
| 88 | 
                + </div>  | 
            |
| 89 | 
                + </div>  | 
            |
| 90 | 
                +            {% endifequal %}
               | 
            |
| 91 | 
                +  | 
            |
| 92 | 
                + <br>  | 
            |
| 93 | 
                +  | 
            |
| 94 | 
                +            {% if modified %}<button id="submit" class="weui_btn weui_btn_warn">确认</button>{% endif %}
               | 
            |
| 95 | 
                +  | 
            |
| 96 | 
                + <div class="weui_dialog_alert" id="dialog" style="display: none">  | 
            |
| 97 | 
                + <div class="weui_mask"></div>  | 
            |
| 98 | 
                + <div class="weui_dialog">  | 
            |
| 99 | 
                + <div class="weui_dialog_hd"><strong id="title" class="weui_dialog_title">弹窗标题</strong></div>  | 
            |
| 100 | 
                + <div id="content" class="weui_dialog_bd">弹窗内容,告知当前页面信息等</div>  | 
            |
| 101 | 
                + <div class="weui_dialog_ft">  | 
            |
| 102 | 
                + <a href="javascript:;" class="weui_btn_dialog primary">确定</a>  | 
            |
| 103 | 
                + </div>  | 
            |
| 104 | 
                + </div>  | 
            |
| 105 | 
                + </div>  | 
            |
| 106 | 
                +  | 
            |
| 107 | 
                + <div id="toast" style="display: none;">  | 
            |
| 108 | 
                + <div class="weui_mask_transparent"></div>  | 
            |
| 109 | 
                + <div class="weui_toast">  | 
            |
| 110 | 
                + <i class="weui_icon_toast"></i>  | 
            |
| 111 | 
                + <p class="weui_toast_content">已完成</p>  | 
            |
| 112 | 
                + </div>  | 
            |
| 113 | 
                + </div>  | 
            |
| 114 | 
                + </div>  | 
            |
| 115 | 
                +  | 
            |
| 116 | 
                + <script src="//cdn.bootcss.com/zepto/1.1.6/zepto.min.js"></script>  | 
            |
| 117 | 
                + <script>  | 
            |
| 118 | 
                +            {% if modified %}
               | 
            |
| 119 | 
                +            $(function() {
               | 
            |
| 120 | 
                +                function getURLParameter(name) {
               | 
            |
| 121 | 
                +                  return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [null, ''])[1].replace(/\+/g, '%20')) || null;
               | 
            |
| 122 | 
                + }  | 
            |
| 123 | 
                +  | 
            |
| 124 | 
                +                function show_error_dialog(title, content) {
               | 
            |
| 125 | 
                +                    $('#dialog #title').text(title);
               | 
            |
| 126 | 
                +                    $('#dialog #content').text(content);
               | 
            |
| 127 | 
                +                    $('#dialog').show();
               | 
            |
| 128 | 
                + }  | 
            |
| 129 | 
                +  | 
            |
| 130 | 
                +                function data_check() {
               | 
            |
| 131 | 
                +                    var unionid = getURLParameter('unionid');
               | 
            |
| 132 | 
                +                    if (!unionid) {
               | 
            |
| 133 | 
                +                        show_error_dialog('微信授权', '微信授权失败,请重新打开页面');
               | 
            |
| 134 | 
                + return false;  | 
            |
| 135 | 
                + }  | 
            |
| 136 | 
                +  | 
            |
| 137 | 
                +                    var name = $('#name').val();
               | 
            |
| 138 | 
                +                    if (!name) {
               | 
            |
| 139 | 
                +                        show_error_dialog('姓名', '姓名错误,请检查重新输入');
               | 
            |
| 140 | 
                + return false;  | 
            |
| 141 | 
                + }  | 
            |
| 142 | 
                +  | 
            |
| 143 | 
                +                    var phone_valid = $('#phone').is(':valid');
               | 
            |
| 144 | 
                +                    if (!phone_valid) {
               | 
            |
| 145 | 
                +                        show_error_dialog('手机号', '手机号错误,请检查重新输入');
               | 
            |
| 146 | 
                + return false;  | 
            |
| 147 | 
                + }  | 
            |
| 148 | 
                +  | 
            |
| 149 | 
                +{#                    var location = $('#location').val();#}
               | 
            |
| 150 | 
                +{#                    if (!location) {#}
               | 
            |
| 151 | 
                +{#                        show_error_dialog('地址', '地址错误,请检查重新输入');#}
               | 
            |
| 152 | 
                +{#                        return false;#}
               | 
            |
| 153 | 
                +{#                    }#}
               | 
            |
| 154 | 
                +  | 
            |
| 155 | 
                +                    var no = $('#no').val();
               | 
            |
| 156 | 
                +                    if (!no) {
               | 
            |
| 157 | 
                +                        show_error_dialog('编号', '导游证编号错误,请检查重新输入');
               | 
            |
| 158 | 
                + return false;  | 
            |
| 159 | 
                + }  | 
            |
| 160 | 
                +  | 
            |
| 161 | 
                +                    return {
               | 
            |
| 162 | 
                + unionid: unionid,  | 
            |
| 163 | 
                +                        openid: getURLParameter('openid'),
               | 
            |
| 164 | 
                + name: name,  | 
            |
| 165 | 
                +                        sex: $('#sex option:checked').val(),
               | 
            |
| 166 | 
                +                        phone: $('#phone').val(),
               | 
            |
| 167 | 
                +{#                        location: location,#}
               | 
            |
| 168 | 
                + no: no,  | 
            |
| 169 | 
                + }  | 
            |
| 170 | 
                + }  | 
            |
| 171 | 
                +  | 
            |
| 172 | 
                +                $('#submit').click(function () {
               | 
            |
| 173 | 
                + var check_result = data_check();  | 
            |
| 174 | 
                +                    if (check_result){
               | 
            |
| 175 | 
                +                        $.ajax({
               | 
            |
| 176 | 
                + type: 'POST',  | 
            |
| 177 | 
                + url: 't/submit',  | 
            |
| 178 | 
                + data: check_result,  | 
            |
| 179 | 
                +                            success: function(data) {
               | 
            |
| 180 | 
                +                                if (data.status == 200) {
               | 
            |
| 181 | 
                +                                    $('#toast').show();
               | 
            |
| 182 | 
                +                                    setTimeout(function () {
               | 
            |
| 183 | 
                +                                        $('#toast').hide();
               | 
            |
| 184 | 
                + }, 1000);  | 
            |
| 185 | 
                + window.location.reload();  | 
            |
| 186 | 
                +                                } else {
               | 
            |
| 187 | 
                +                                    show_error_dialog('错误', data.description);
               | 
            |
| 188 | 
                + }  | 
            |
| 189 | 
                + }  | 
            |
| 190 | 
                + })  | 
            |
| 191 | 
                + }  | 
            |
| 192 | 
                + });  | 
            |
| 193 | 
                +  | 
            |
| 194 | 
                +                $('#dialog .weui_btn_dialog').click(function () {
               | 
            |
| 195 | 
                +                    $('#dialog').hide();
               | 
            |
| 196 | 
                + })  | 
            |
| 197 | 
                + });  | 
            |
| 198 | 
                +            {% endif %}
               | 
            |
| 199 | 
                + </script>  | 
            |
| 200 | 
                + <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>  | 
            |
| 201 | 
                +        <script type="text/javascript" src="{% static 'pai2/js/jswe.js' %}?v=1"></script>
               | 
            |
| 202 | 
                + <script>  | 
            |
| 203 | 
                +            V.initWxData({
               | 
            |
| 204 | 
                + imgUrl: "http://pai.ai/static/pai2/img/paiai_96_96.png",  | 
            |
| 205 | 
                + link: 'http://api.pai.ai/wx_oauth2?redirect_url=http://pai.ai/page/tourguide&scope=snsapi_base',  | 
            |
| 206 | 
                + desc: "导游授权",  | 
            |
| 207 | 
                + title: "导游授权",  | 
            |
| 208 | 
                + timeLine: ""  | 
            |
| 209 | 
                + }, true);  | 
            |
| 210 | 
                + V.hideOptionMenu();  | 
            |
| 211 | 
                + </script>  | 
            |
| 212 | 
                + </body>  | 
            |
| 213 | 
                +</html>  | 
            
                @@ -2,7 +2,7 @@  | 
            ||
| 2 | 2 | 
                 | 
            
| 3 | 3 | 
                from django.shortcuts import render  | 
            
| 4 | 4 | 
                 | 
            
| 5 | 
                -from account.models import LensmanInfo  | 
            |
| 5 | 
                +from account.models import LensmanInfo, TourGuideInfo  | 
            |
| 6 | 6 | 
                 | 
            
| 7 | 7 | 
                 | 
            
| 8 | 8 | 
                def user_agreement(request):  | 
            
                @@ -29,3 +29,17 @@ def lensman_oauth(request):  | 
            ||
| 29 | 29 | 
                 | 
            
| 30 | 30 | 
                def lensman_price(request):  | 
            
| 31 | 31 | 
                     return render(request, 'page/lensman_price.html', {})
               | 
            
| 32 | 
                +  | 
            |
| 33 | 
                +  | 
            |
| 34 | 
                +def tourguide_oauth(request):  | 
            |
| 35 | 
                +    unionid = request.GET.get('unionid', '')
               | 
            |
| 36 | 
                +  | 
            |
| 37 | 
                + try:  | 
            |
| 38 | 
                + tourguide = TourGuideInfo.objects.get(unionid=unionid)  | 
            |
| 39 | 
                + except TourGuideInfo.DoesNotExist:  | 
            |
| 40 | 
                + tourguide = None  | 
            |
| 41 | 
                +  | 
            |
| 42 | 
                +    return render(request, 'page/tourguide_oauth.html', {
               | 
            |
| 43 | 
                + 'tourguide_info': tourguide and tourguide.data,  | 
            |
| 44 | 
                + 'modified': bool((not tourguide) or (tourguide and tourguide.user_status in [TourGuideInfo.UNVERIFIED, TourGuideInfo.REFUSED])), # 是否可以更改信息  | 
            |
| 45 | 
                + })  | 
            
                @@ -22,6 +22,7 @@ from django.contrib import admin  | 
            ||
| 22 | 22 | 
                from rest_framework import routers  | 
            
| 23 | 23 | 
                 | 
            
| 24 | 24 | 
                from account import views as account_views  | 
            
| 25 | 
                +from account import tourguide_views  | 
            |
| 25 | 26 | 
                from group import views as group_views  | 
            
| 26 | 27 | 
                from group import lensman_views  | 
            
| 27 | 28 | 
                from page import views as page_views  | 
            
                @@ -69,6 +70,8 @@ urlpatterns += [  | 
            ||
| 69 | 70 | 
                 | 
            
| 70 | 71 | 
                url(r'^page/lensman$', page_views.lensman_oauth, name='lensman_oauth'), # 摄影师授权页面  | 
            
| 71 | 72 | 
                url(r'^page/price$', page_views.lensman_price, name='lensman_price'), # 摄影师照片价格和分成规则  | 
            
| 73 | 
                +  | 
            |
| 74 | 
                + url(r'^page/tourguide$', page_views.tourguide_oauth, name='tourguide_oauth'), # 导游授权页面  | 
            |
| 72 | 75 | 
                ]  | 
            
| 73 | 76 | 
                 | 
            
| 74 | 77 | 
                urlpatterns += [  | 
            
                @@ -84,6 +87,11 @@ urlpatterns += [  | 
            ||
| 84 | 87 | 
                url(r'^page/l/submit$', lensman_views.lensman_submit_api, name='lensman_submit_api'), # 摄影师信息提交  | 
            
| 85 | 88 | 
                ]  | 
            
| 86 | 89 | 
                 | 
            
| 90 | 
                +# 导游相关  | 
            |
| 91 | 
                +urlpatterns += [  | 
            |
| 92 | 
                + url(r'^page/t/submit$', tourguide_views.tourguide_submit_api, name='tourguide_submit_api'), # 导游信息提交  | 
            |
| 93 | 
                +]  | 
            |
| 94 | 
                +  | 
            |
| 87 | 95 | 
                # Wire up our API using automatic URL routing.  | 
            
| 88 | 96 | 
                # Additionally, we include login URLs for the browsable API.  | 
            
| 89 | 97 | 
                urlpatterns += [  | 
            
                @@ -15,25 +15,37 @@ class LensmanStatusCode(BaseStatusCode):  | 
            ||
| 15 | 15 | 
                LENSMAN_NOT_ACTIVATED = StatusCodeField(400015, u'Lensman Not Activated', description=u'摄影师帐号未激活')  | 
            
| 16 | 16 | 
                 | 
            
| 17 | 17 | 
                 | 
            
| 18 | 
                +class TourGuideStatusCode(BaseStatusCode):  | 
            |
| 19 | 
                + """ 导游相关错误码 4001xx """  | 
            |
| 20 | 
                + TOURGUIDE_NOT_FOUND = StatusCodeField(400101, u'Tour Guide Not Found', description=u'导游不存在')  | 
            |
| 21 | 
                + TOURGUIDE_PASSWORD_ERROR = StatusCodeField(400102, u'Tour Guide Password Error', description=u'导游密码错误')  | 
            |
| 22 | 
                +  | 
            |
| 23 | 
                + TOURGUIDE_PHONE_ALREADY_EXISTS = StatusCodeField(400105, u'Tour Guide Phone Already Exists', description=u'手机号已经存在')  | 
            |
| 24 | 
                +  | 
            |
| 25 | 
                + TOURGUIDE_ALREADY_NOT_UNVERIFIED = StatusCodeField(400110, u'Tour Guide Already Not Unverified', description=u'导游帐号已激活')  | 
            |
| 26 | 
                +  | 
            |
| 27 | 
                + TOURGUIDE_NOT_ACTIVATED = StatusCodeField(400115, u'Tour Guide Not Activated', description=u'导游帐号未激活')  | 
            |
| 28 | 
                +  | 
            |
| 29 | 
                +  | 
            |
| 18 | 30 | 
                class UserStatusCode(BaseStatusCode):  | 
            
| 19 | 
                - """ 用户相关错误码 4001xx """  | 
            |
| 20 | 
                - USER_NOT_FOUND = StatusCodeField(400101, u'User Not Found', description=u'用户不存在')  | 
            |
| 21 | 
                - USER_PASSWORD_ERROR = StatusCodeField(400102, u'User Password Error', description=u'用户密码错误')  | 
            |
| 22 | 
                - USERNAME_HAS_REGISTERED = StatusCodeField(400103, u'Username Has Registered', description=u'用户名已注册')  | 
            |
| 31 | 
                + """ 用户相关错误码 4005xx """  | 
            |
| 32 | 
                + USER_NOT_FOUND = StatusCodeField(400501, u'User Not Found', description=u'用户不存在')  | 
            |
| 33 | 
                + USER_PASSWORD_ERROR = StatusCodeField(400502, u'User Password Error', description=u'用户密码错误')  | 
            |
| 34 | 
                + USERNAME_HAS_REGISTERED = StatusCodeField(400503, u'Username Has Registered', description=u'用户名已注册')  | 
            |
| 23 | 35 | 
                 | 
            
| 24 | 
                - GUEST_NOT_ALLOWED = StatusCodeField(400111, u'Guest Not ALLOWED', description=u'游客登录未开启')  | 
            |
| 36 | 
                + GUEST_NOT_ALLOWED = StatusCodeField(400511, u'Guest Not ALLOWED', description=u'游客登录未开启')  | 
            |
| 25 | 37 | 
                 | 
            
| 26 | 38 | 
                 | 
            
| 27 | 39 | 
                class PhoneStatusCode(BaseStatusCode):  | 
            
| 28 | 
                - """ 手机相关错误码 4002xx """  | 
            |
| 29 | 
                - PHONE_NOT_FOUND = StatusCodeField(400201, u'Phone Not Found', description=u'手机不存在')  | 
            |
| 40 | 
                + """ 手机相关错误码 4006xx """  | 
            |
| 41 | 
                + PHONE_NOT_FOUND = StatusCodeField(400601, u'Phone Not Found', description=u'手机不存在')  | 
            |
| 30 | 42 | 
                 | 
            
| 31 | 43 | 
                 | 
            
| 32 | 44 | 
                class WechatStatusCode(BaseStatusCode):  | 
            
| 33 | 
                - """ 微信相关错误码 4003xx """  | 
            |
| 34 | 
                - WECHAT_NOT_FOUND = StatusCodeField(400301, u'Wechat Not Found', description=u'微信不存在')  | 
            |
| 35 | 
                - UNIONID_NOT_FOUND = StatusCodeField(400302, u'Unionid Not Found', description=u'微信 UNIONID 不存在')  | 
            |
| 36 | 
                - OPENID_NOT_FOUND = StatusCodeField(400303, u'OPENID Not Found', description=u'微信 OPENID 不存在')  | 
            |
| 45 | 
                + """ 微信相关错误码 4007xx """  | 
            |
| 46 | 
                + WECHAT_NOT_FOUND = StatusCodeField(400701, u'Wechat Not Found', description=u'微信不存在')  | 
            |
| 47 | 
                + UNIONID_NOT_FOUND = StatusCodeField(400702, u'Unionid Not Found', description=u'微信 UNIONID 不存在')  | 
            |
| 48 | 
                + OPENID_NOT_FOUND = StatusCodeField(400703, u'OPENID Not Found', description=u'微信 OPENID 不存在')  | 
            |
| 37 | 49 | 
                 | 
            
| 38 | 50 | 
                 | 
            
| 39 | 51 | 
                class PhotoStatusCode(BaseStatusCode):  |